package com.atguigu.gmall.common.config;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.entity.GmallCorrelationData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * 这个类实现了ConfirmCallback , ReturnCallback 这两个RedisTemplate类的内置接口
 * 功能: 实现mq的应答: 交换机的应答  队列的应答
 */
//
@Component
@Slf4j
public class MQProducerAckConfig implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    //后置
    @PostConstruct
    public void MQProducerAckConfig(){
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnCallback(this);
    }

    //交换机应答: 成功也应答 失败也应答
    @Override         //correlationData: 应答返回的数据
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (ack){
            //成功应答
            log.info("交换机成功接收到了消息:"+ JSONObject.toJSONString(correlationData));
        }else {
            //失败应答
            log.error("交换机没有接收到消息,原因是:"+cause);
            //重新发送消息,如果3次都不成功,转人工处理
            reTrySendMessage(correlationData);
        }
    }

    //重新发信息次数: 3次 不允许无限消息   如果指定次数还是发不成功 打日志转人工处理
    private void reTrySendMessage(CorrelationData correlationData){
        GmallCorrelationData gmallCorrelationData = (GmallCorrelationData) correlationData;
        //判断发送次数, 不能超过3次
        int retryCount = gmallCorrelationData.getRetryCount();
        if (retryCount < 3){
            retryCount++;
            //设置重发次数
            gmallCorrelationData.setRetryCount(retryCount);

            //更新redis中的缓存数据
            redisTemplate.opsForValue().set(gmallCorrelationData.getId(),
                    JSONObject.toJSONString(gmallCorrelationData));

            //重新发送信息
            rabbitTemplate.convertAndSend(gmallCorrelationData.getExchange(),
                    gmallCorrelationData.getRoutingKey(),
                    gmallCorrelationData.getMessage(),
                    correlationData);
        }else {
            //发送的次数已经耗尽了  {} : 占位符
            log.error("发送的次数已经耗尽了:{}",JSONObject.toJSONString(gmallCorrelationData));
        }

    }

    //queue队列应答: 只有失败时应答
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText,
                                String exchange, String routingKey) {
        log.error("队列失败应答");
        log.error("消费者的内容是: "+ new String(message.getBody())); //如果是别的类型转换成string有问题
        log.error("交换机是:"+exchange);
        log.error("routingKey是:"+routingKey);
        log.error("应答Code是：" + replyCode);
        log.error("应答Text是：" + replyText);

        //当队列应答失败时,发送3次消息 ,使用redis来实现
        //获取message请求头信息里面的uuid
        String uuid = (String)message.getMessageProperties().
                getHeaders().get("spring_returned_message_correlation");
        log.info("队列应答失败了:uuid:"+uuid);
        //使用redis获取gmallCorrelationData
        String gmallCorrelationDataJson = (String) redisTemplate.opsForValue().get(uuid);
        log.info("队列应答失败了: json:"+gmallCorrelationDataJson);

        //准备出来关联数据对象
        GmallCorrelationData gmallCorrelationData = JSONObject.parseObject(gmallCorrelationDataJson, GmallCorrelationData.class);

        //判断是否为延迟消息
        if (!gmallCorrelationData.isDelay()){
            //如果不是延迟消息
            //重新发送消息
            reTrySendMessage(gmallCorrelationData);
        }else {
            //如果是延迟消息
            log.info("是延迟消息触发的队列应答失败,可以选择不重新发送消息: 交换机:{},RoutingKey：{}"
            + exchange, routingKey);
            //要是延迟消息真的发送失败了,就没办法重新发送延迟消息了
        }

    }

    @Autowired
    private RedisTemplate redisTemplate;
}
